package com.tencent.sr.iris.activity.service.event.job;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.tencent.sr.iris.activity.common.enums.UserTaskRecordStatusEnum;
import com.tencent.sr.iris.activity.common.enums.UserTaskStatusEnum;
import com.tencent.sr.iris.activity.common.util.ShardingIdUtil;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityCheckResultDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityDTO;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityEventTypeEnum;
import com.tencent.sr.iris.activity.interfaces.event.dto.ActivityEventDTO;
import com.tencent.sr.iris.activity.repository.Criteria;
import com.tencent.sr.iris.activity.repository.entity.TIrisEventHandingRecordDO;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserTaskRecordDO;
import com.tencent.sr.iris.activity.repository.mapper.TIrisEventHandingRecordMapper;
import com.tencent.sr.iris.activity.repository.mapper.TIrisUserTaskRecordMapper;
import com.tencent.sr.iris.activity.repository.mapper.example.TIrisEventHandingRecordExample;
import com.tencent.sr.iris.activity.repository.mapper.example.TIrisUserTaskRecordExample;
import com.tencent.sr.iris.activity.service.activity.IrisActivityService;
import com.tencent.sr.iris.activity.service.activity.IrisActivityTaskService;
import com.tencent.sr.iris.activity.service.activity.IrisUserActivityRecordService;
import com.tencent.sr.iris.activity.service.activity.IrisUserTaskRecordService;
import com.tencent.sr.iris.activity.service.event.EventExecutorContext;
import com.tencent.sr.iris.activity.service.event.activity.AbstractIrisActivityExecutor;
import com.tencent.sr.iris.activity.service.event.task.IrisTaskExecutor;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.rmall.common.exception.retail.TRetailErrorException;
import com.tencent.sr.rmall.springbootstarter.elasticjob.TaskJob;
import com.tencent.sr.rmall.springbootstarter.monitor.util.MonitorUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 用户参与任务记录状态处理，获取一个最近的事件，重新触发流程
 *
 * @author xiaojun
 */
@Slf4j
@Component
//@TaskJob(jobName = "userActivityTaskRecordStatusFixJob", cron = "0 */3 * * * ?", description = "用户任务超期且处于进行中，关闭任务")
@TaskJob(jobName = "userActivityTaskRecordStatusFixJob", cron = "0 10 0 */1 * ?", description = "用户任务超期且处于进行中，关闭任务")
public class UserActivityTaskRecordStatusFixJob implements SimpleJob {

    @Resource
    private RedissonClient redissonClient;
    @Resource
    private TIrisEventHandingRecordMapper tIrisEventHandingRecordMapper;
    @Resource
    private TIrisUserTaskRecordMapper iIrisUserTaskRecordMapper;
    @Resource
    private IrisActivityTaskService irisActivityTaskService;
    @Resource
    private EventExecutorContext eventExecutorContext;
    @Resource
    private IrisActivityService irisActivityService;
    @Resource
    private IrisUserTaskRecordService userTaskRecordService;
    @Resource
    private IrisUserActivityRecordService activityRecordService;
    @Resource
    private ActivityCommonUtilService activityCommonUtilService;

    private static final int MAX_SHARDING_ID = 1023;

    /**
     * 分布式锁最大执行时间20分钟
     */
    private static final int MAX_LOCK_TIME = 20;

    /**
     * 分页大小
     */
    private static final int LIMIT = 500;

    @Override
    public void execute(ShardingContext shardingContext) {
        log.info("用户参与任务状态变更任务：start");
        long startTime = System.currentTimeMillis();
        Throwable throwable = null;
        RLock lock = null;
        try {
            lock = redissonClient.getLock(getLockKey());

            if (!lock.tryLock(MAX_LOCK_TIME, TimeUnit.MINUTES)) {
                log.info("用户参与任务状态变更任务：有任务正在执行中，本次忽略扫表");
                return;
            }

            for (int i = 0; i <= MAX_SHARDING_ID; i++) {

                //记录每个shard最大循环次数
                int loopTimes = 0;
                int offset = 0;
                List<TIrisUserTaskRecordDO> needUpdateRecords = null;
                do {
                    loopTimes++;
                    needUpdateRecords = queryNeedUpdateRecords(i, offset, LIMIT);

                    if (CollectionUtil.isNotEmpty(needUpdateRecords)) {
                        needUpdateRecords.stream().forEach(recordDO -> doExecute(recordDO));
                    }
                    log.info("用户参与任务状态变更任务：shardingId:{},总数:{}", i, needUpdateRecords.size());
                    offset += LIMIT;
                } while (CollectionUtil.isNotEmpty(needUpdateRecords) && needUpdateRecords.size() == LIMIT
                        && loopTimes < 10);
            }
        } catch (Exception e) {
            log.error("用户参与任务状态变更任务：执行异常", e);
            throwable = e;
        } finally {
            if (lock != null) {
                lock.unlock();
            }
            log.info("用户参与任务状态变更任务:end");
            MonitorUtil.getSingleMonitorService().logTransaction("activity_job", "userActivityTaskRecordStatusFixJob",
                    startTime, throwable);
        }

    }

    /**
     * 重跑任务时需要排查的事件类型
     */
    private static final ArrayList<String> excludeEventTypes = Lists
            .newArrayList(ActivityEventTypeEnum.SPECIAL_INVITE.getCode(),
                    ActivityEventTypeEnum.SUPERVISOR_CHANGE.getCode());

    private void doExecute(TIrisUserTaskRecordDO userTaskRecordDO) {

        String uid = userTaskRecordDO.getUid();
        String pid = userTaskRecordDO.getPid();

        TIrisEventHandingRecordExample example = new TIrisEventHandingRecordExample();

        example.createCriteria().andPropertyEqualTo(TIrisEventHandingRecordExample.SHARDING_ID,
                ShardingIdUtil.doSharding(uid))
                .andPropertyEqualTo(TIrisEventHandingRecordExample.UID, uid)
                .andPropertyEqualTo(TIrisEventHandingRecordExample.TASK_ID, userTaskRecordDO.getTaskId())
                .andPropertyEqualTo(TIrisEventHandingRecordExample.ACTIVITY_ID, userTaskRecordDO.getActivityId())
                .andPropertyNotIn(TIrisEventHandingRecordExample.EVENT_TYPE, excludeEventTypes)
                .andPropertyLessThanOrEqualTo(TIrisEventHandingRecordExample.EVENT_TIME,
                        formatEndTime(userTaskRecordDO.getCurrentTaskEndTime()))
                .andPropertyGreaterThanOrEqualTo(TIrisEventHandingRecordExample.EVENT_TIME,
                        formatStartTime(userTaskRecordDO.getCurrentTaskStartTime()))
        ;
        example.setOrderByClause("create_time desc");

        List<TIrisEventHandingRecordDO> recordDOList = tIrisEventHandingRecordMapper.limitSelectByExample(
                example, 0, 1);
        if (CollectionUtil.isEmpty(recordDOList)) {
            log.info("更新任务状态：没查到任务参与记录对应的事件信息，忽略,uid:{},taskId:{},userTaskRecord bizId:{}", uid,
                    userTaskRecordDO.getTaskId(), userTaskRecordDO.getBizId());
            return;
        }
        TIrisEventHandingRecordDO handingRecordDO = recordDOList.get(0);
        String eventInfoJson = handingRecordDO.getEventInfo();

        ActivityEventDTO eventDTO = JSON.parseObject(eventInfoJson, ActivityEventDTO.class);
        if (Strings.isNullOrEmpty(eventDTO.getIdempotent())) {
            log.info("更新任务状态：忽略垃圾数据,eventDTO:{}", JSON.toJSONString(eventDTO));
            return;
        }

        String activityBizId = handingRecordDO.getActivityId();
        ActivityDTO activityDTO = irisActivityService.getActivity(activityBizId);
        String taskBizId = handingRecordDO.getTaskId();
        ActivityTaskDTO taskDTO = irisActivityTaskService.getTaskByTaskBizId(taskBizId);

        AbstractIrisActivityExecutor activityHandler = (AbstractIrisActivityExecutor) eventExecutorContext
                .getActivityHandler(activityBizId);
        if (activityHandler == null) {
            log.error("更新任务状态：找不到对应的IrisActivityExecutor,activityBizId:{}", activityBizId);
            return;
        }
        //先校验活动门槛，如果不满足活动门槛，删除进行中的任务
        try {
            ActivityCheckResultDTO accessConditionCheck = activityHandler.doAccessConditionCheck(activityDTO, eventDTO);
            if (!accessConditionCheck.isSatisfy()
                    && UserTaskRecordStatusEnum.NORMAL.getCode() == userTaskRecordDO.getUserTaskStatus()) {
                //如果不满足活动任务门槛，删除已存在的进行中任务记录
                int i = userTaskRecordService.deleteInProgressTaskRecord(uid, activityBizId,
                        taskBizId);
                log.error("更新任务状态：当前用户不满足活动准入规则，删除进行中的任务,uid:{}，taskBizId:{}, 删除记录数:{}", uid,
                        taskBizId, i);
                activityRecordService.handleInProgressActivityRecord(uid,activityDTO.getBizId());
                return;
            }
        } catch (Exception e) {
            log.error("更新任务状态：校验任务的活动准入规则异常,uid:{},taskId:{}", uid, taskBizId, e);
            return;
        }

        RLock rLock = null;
        try {
            rLock = activityCommonUtilService.inviteGiftTryLock(activityBizId, pid, uid);
            IrisTaskExecutor taskHandler = eventExecutorContext.getTaskHandler(taskBizId);
            if (taskHandler == null) {
                log.error("更新任务状态：找不到对应的IrisTaskExecutor,taskBizId:{}", taskBizId);
                return;
            }
            taskHandler.execute(eventDTO, activityDTO, taskDTO, null);
            log.info("更新任务状态:task：发起成功,uid:{},handingRecord bizId:{},taskBizId:{}", handingRecordDO.getUid(),
                    handingRecordDO.getBizId(), taskBizId);

            activityRecordService.addOrUpdateActivityRecord(eventDTO.getUid(), eventDTO.getEventTime(), activityDTO.getBizId(),
                    activityDTO.getPeriodType(), activityDTO.getPeriod());
        } catch (Exception e) {
            log.error("邀请有礼事件重试失败：", e);
            throw new TRetailErrorException(e);
        } finally {
            if (rLock != null && rLock.isLocked() && rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }

    /**
     * 查询进行中，且创建时间3.1号之后，任务周期已结束的任务
     *
     * @param i
     * @param offset
     * @param limit
     * @return
     */
    private List<TIrisUserTaskRecordDO> queryNeedUpdateRecords(int i, int offset, int limit) {
        TIrisUserTaskRecordExample example = new TIrisUserTaskRecordExample();
        example.createCriteria()
                .andPropertyEqualTo(TIrisUserTaskRecordExample.SHARDING_ID, i)
                .andPropertyEqualTo(TIrisUserTaskRecordExample.STATUS, UserTaskRecordStatusEnum.NORMAL.getCode())
                .andPropertyLessThan(TIrisUserTaskRecordExample.CURRENT_TASK_END_TIME, LocalDate.now())
                .andPropertyEqualTo(TIrisUserTaskRecordExample.USER_TASK_STATUS,
                        UserTaskStatusEnum.PROGRESS.getCode());

        List<TIrisUserTaskRecordDO> doList = iIrisUserTaskRecordMapper.limitSelectByExample(example,
                offset, limit);
        return doList;
    }


    private String getLockKey() {
        return "userActivityTaskRecordStatusFixJob_lock";
    }

    public void doExecuteUid(String uid, String taskRecordBizId) {
        TIrisUserTaskRecordExample example = new TIrisUserTaskRecordExample();
        Criteria criteria = example.createCriteria();
        criteria
                .andPropertyEqualTo(TIrisUserTaskRecordExample.SHARDING_ID, ShardingIdUtil.doSharding(uid))
                .andPropertyEqualTo(TIrisUserTaskRecordExample.STATUS, UserTaskRecordStatusEnum.NORMAL.getCode())
                .andPropertyEqualTo(TIrisUserTaskRecordExample.USER_TASK_STATUS,
                        UserTaskStatusEnum.PROGRESS.getCode())
                .andPropertyEqualTo(TIrisUserTaskRecordExample.UID, uid)

        ;
        if (StringUtils.isNotEmpty(taskRecordBizId)) {
            criteria.andPropertyEqualTo(TIrisUserTaskRecordExample.BIZ_ID, taskRecordBizId);
        }

        List<TIrisUserTaskRecordDO> doList = iIrisUserTaskRecordMapper.limitSelectByExample(example,
                0, 20);
        if (CollectionUtil.isNotEmpty(doList)) {
            for (TIrisUserTaskRecordDO record : doList) {
                doExecute(record);
                log.info("更新任务状态：手动重试 uid:{},taskId{}", record.getUid(), record.getTaskId());
            }
        }
        return;
    }


    private static Date formatStartTime(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.HOUR_OF_DAY, 0);
        instance.set(Calendar.MINUTE, 0);
        instance.set(Calendar.SECOND, 0);
        return instance.getTime();
    }

    private static Date formatEndTime(Date date) {
        Calendar instance = Calendar.getInstance();
        instance.setTime(date);
        instance.set(Calendar.HOUR_OF_DAY, 23);
        instance.set(Calendar.MINUTE, 59);
        instance.set(Calendar.SECOND, 59);
        return instance.getTime();
    }
}
